package org.internna.ossmoney.util;

import java.util.List;
import java.util.Date;
import java.util.Random;
import java.util.Calendar;
import java.util.ArrayList;

/**
 * Utilities to work with Dates.
 *
 */
public final class DateUtils {

    private static final Random random = new Random();

    /**
     * Default private constructor for a Utility class.
     */
    private DateUtils() {
        throw new AssertionError("Do not try to instantiate utility class");
    }

    public static Date[] dates(int months) {
    	Date[] dates = new Date[months + 1];
    	Calendar calendar = Calendar.getInstance();
    	dates[months] = end(calendar).getTime();
    	for (int month = months; month > 0; month--) {
            calendar.add(Calendar.MONTH, -1);
            calendar = getMonthEndDate(calendar);
            dates[month - 1] = calendar.getTime();
    	}
    	return dates;
    }

    public static Calendar end(Calendar calendar) {
    	calendar.set(Calendar.MINUTE, 59);
        calendar.set(Calendar.SECOND, 59);
        calendar.set(Calendar.HOUR_OF_DAY, 23);
        calendar.set(Calendar.MILLISECOND, 999);
        return calendar;
    }

    public static Date getMonthEndDate(Date month) {
    	Calendar calendar = Calendar.getInstance();
    	if (month != null) {
            calendar.setTime(month);
        }
    	return getMonthEndDate(calendar).getTime();
    }
    
    public static Calendar getMonthEndDate(Calendar calendar) {
    	Calendar cal = calendar != null ? calendar : Calendar.getInstance();
    	cal.set(Calendar.DAY_OF_MONTH, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
    	return end(cal);
    }

    public static Date getMonthStartDate(Date month) {
    	Calendar calendar = Calendar.getInstance();
    	if (month != null) {
            calendar.setTime(month);
        }
    	getMonthStartDate(calendar);
    	return calendar.getTime();
    }

    public static Calendar getMonthStartDate(Calendar calendar) {
        calendar.set(Calendar.DAY_OF_MONTH, 1);
        return getMidnight(calendar);
    }

    /**
     * Obtains a date just after the provided one.
     *
     * @param date any
     * @return a non null instance
     */
    public static Date nextDate(Date date) {
        Calendar calendar = Calendar.getInstance();
        if (date != null) {
            calendar.setTime(date);
            calendar.add(Calendar.MILLISECOND, 1);
        }
        return calendar.getTime();
    }

    /**
     * Sets a random time for a given date.
     *
     * @param operationDate any
     * @return any
     */
    public static Date getRandomTimeDate(Date operationDate) {
    	if (operationDate != null) {
	        Calendar calendar = Calendar.getInstance();
	        calendar.setTime(operationDate);
	        calendar.set(Calendar.SECOND, random.nextInt(59));
	        calendar.set(Calendar.MINUTE, random.nextInt(59));
	        calendar.set(Calendar.HOUR_OF_DAY, random.nextInt(isToday(operationDate) ? getHour(operationDate) : 23));
	        calendar.set(Calendar.MILLISECOND, random.nextInt(999));
	        return calendar.getTime();
    	}
    	return null;
    }

    /**
     * Checks if a date is today.
     *
     * @param date any
     * @return true if the day of the input is the same as today
     */
    public static boolean isToday(Date date) {
    	boolean isToday = false;
    	if (date != null) {
    		Calendar today = Calendar.getInstance();
    		Calendar check = Calendar.getInstance();
    		check.setTime(date);
    		isToday |= (today.get(Calendar.ERA) == check.get(Calendar.ERA)) && (today.get(Calendar.YEAR) == check.get(Calendar.YEAR)) && (today.get(Calendar.DAY_OF_YEAR) == check.get(Calendar.DAY_OF_YEAR));
    	}
    	return isToday;
    }

    public static int getHour(Date date) {
    	int hour = 1;
    	if (date != null) {
    		Calendar calendar = Calendar.getInstance();
    		calendar.setTime(date);
    		hour = calendar.get(Calendar.HOUR_OF_DAY);
    	}
    	return Math.min(Math.max(1, hour), 23);
    }

    public static int getMonth(Date date) {
    	int month = 0;
    	if (date != null) {
    		Calendar calendar = Calendar.getInstance();
    		calendar.setTime(date);
    		month = calendar.get(Calendar.MONTH);
    	}
    	return Math.min(Math.max(0, month), 11);
    }

    /**
     * Sets 00:00:00.000 as the time of the given date.
     *
     * @param operationDate any
     * @return any
     */
    public static Date getMidnight(Date operationDate) {
    	if (operationDate != null) {
	    	Calendar calendar = Calendar.getInstance();
			calendar.setTime(operationDate);
			return getMidnight(calendar).getTime();
    	}
    	return null;
    }

    private static Calendar getMidnight(Calendar calendar) {
    	calendar.set(Calendar.MINUTE, 0);
		calendar.set(Calendar.SECOND, 0);
		calendar.set(Calendar.HOUR_OF_DAY, 0);
		calendar.set(Calendar.MILLISECOND, 0);
		return calendar;
    }

    /**
     * Adds some days to a date.
     *
     * @param date any
     * @param days any
     * @return any
     */
    public static Date add(Date date, int days) {
    	Calendar calendar = null;
    	if (date != null) {
    		calendar = Calendar.getInstance();
    		calendar.setTime(date);
    		calendar.add(Calendar.DAY_OF_MONTH, days);
    	}
    	return calendar.getTime();
    }

    public static int getMonths(Date from, Date to) {
    	Calendar calendar = Calendar.getInstance();
    	Calendar otherCalendar = Calendar.getInstance();
    	calendar.setTime(from);
    	otherCalendar.setTime(to);
    	int months = otherCalendar.get(Calendar.MONTH) - calendar.get(Calendar.MONTH);
    	return months < 0 ? months + 12 : months;
    }

    public static String asQIF(Date date) {
    	StringBuilder builder = new StringBuilder("D");
    	if (date != null) {
    		Calendar calendar = Calendar.getInstance();
    		calendar.setTime(date);
    		String day = String.valueOf(calendar.get(Calendar.DAY_OF_MONTH));
    		if (day.length() < 2) {
    			builder.append("0");
    		}
    		builder.append(day).append("/");
    		String month = String.valueOf(calendar.get(Calendar.MONTH) + 1);
    		if (month.length() < 2) {
    			builder.append("0");
    		}
    		builder.append(month).append("'").append(calendar.get(Calendar.YEAR));
    	}
    	return builder.toString();
    }

    public static Date[] interval(Date origin, Date target) {
    	List<Date> dates = new ArrayList<Date>();
    	if ((origin != null) && (target != null)) {
	    	Calendar init = Calendar.getInstance();
	    	Calendar finish = Calendar.getInstance();
	    	init.setTime(origin);
	    	init.add(Calendar.DAY_OF_YEAR, 1);
	    	finish.setTime(target);
	    	while (init.before(finish)) {
	    		dates.add(init.getTime());
	    		init.add(Calendar.DAY_OF_YEAR, 1);
	    	}
    	}
    	return dates.toArray(new Date[dates.size()]);
    }

    /**
     * Provides a Date in the past (at least many years ago).
     *
     * @return a valid date
     */
    public static Date getBeginningOfTime() {
    	Calendar calendar = Calendar.getInstance();
    	calendar.set(Calendar.YEAR, 1900);
    	return calendar.getTime();
    }

}
